#include <stdio.h>   
#include <string.h>   
#include <stdlib.h>  
#include <unistd.h>
#include <sys/stat.h>
#include <syslog.h>

#include "cms_common.h"
#include "cms_log.h"
#include "libmdm.h"
#include "cms_alarm.h"

#define ENCRYKEY 0x20191226

#define ENCRYPMSG "GALACHIP_ENCRYPT"

#if 0
static char *encryptXml(void)
{
	char curXmlPath[64] = {0};
	char defXmlPath[64] = {0};
	char *encXmlPath = "/userconfig/config/config.encrypt.xml";
	FILE *fpIn = NULL, *fpOut = NULL;
	struct stat fileBuf;
	char data[1024] = {0};
	uint32_t len = 0;
	int i;

	sprintf(curXmlPath, "%s", CMS_CUR_FILE_XML);
	sprintf(defXmlPath, "%s", CMS_DEF_FILE_XML);

	if (stat(curXmlPath, &fileBuf) < 0){
		fpIn = fopen(defXmlPath, "r");
		if(fpIn == NULL)
			return NULL;
	}else{
		if(fileBuf.st_size == 0){
			fpIn = fopen(defXmlPath, "r");
			if(fpIn == NULL)
				return NULL;
		}else{
			fpIn = fopen(curXmlPath, "r");
			if(fpIn == NULL)
				return NULL;
		}
	}

	fpOut = fopen(encXmlPath, "w+");
	if(fpOut == NULL){
		fclose(fpIn);
		return NULL;
	}

	while(fgets(data, 1024, fpIn) != NULL){
		len = strlen(data);

		for(i = 0; i < len; i++){
			data[i] = data[i] ^ ENCRYKEY;
		}

		if(data[len-1] == ',')
			data[len-1] = '\0';

		fputs(data, fpOut);
		fputs("\n", fpOut);

		memset(data, 0, sizeof(data));
	}

	fclose(fpOut);
	fclose(fpIn);

	return encXmlPath;
}

static int decryptXml(char *xmlPath)
{
	FILE *fpIn = NULL, *fpOut = NULL;
	char data[1024] = {0};
	char tmpData[1024] = {0};
	uint32_t len = 0;
	static int flag = 0;
	int i = 0, j = 0;
	

	if(xmlPath == NULL){
		CMS_LOG(LL_ERROR, "xmlPath is NULL.\n");
		return -1;
	}
	
	fpIn = fopen(xmlPath, "r");
	if(fpIn == NULL){
		CMS_LOG(LL_ERROR, "xmlPath can't open, %s.\n", xmlPath);
		return -1;
	}
	

	fpOut = fopen("/tmp/config.decrypt.xml", "w+");
	if(fpOut == NULL){
		CMS_LOG(LL_ERROR, "xmlout can't open.\n");
		fclose(fpIn);
		return -1;
	}
	

	while(fgets(data, 1024, fpIn) != NULL){
		len = strlen(data);

		/*remove \r from string to compatible with  windows*/
		if(data[len-2] == '\r'){
			j = 0;
			for(i = 0; i < len; i++){
				if(i != len -2)
					tmpData[j++] = data[i]; 
			}
			memset(data, 0, sizeof(data));
			strcpy(data, tmpData);
			len = strlen(data);
		}
		
		for(i = 0; i < len; i++){
			data[i] = data[i] ^ ENCRYKEY;
		}
		
		if(flag == 0){
			if(strstr(data,"xml version") && strstr(data, "encoding") && strstr(data, "utf-8")){
				CMS_LOG(LL_FLOOD, "check ok\n");
				flag = 1;
			}else{
				fclose(fpOut);
				fclose(fpIn);
				return -1;
			}
		}

		if((data[len-1] == ',') && (data[len-2] == '>')){
			strncpy(tmpData, data, len-1);
			fputs(tmpData, fpOut);
			fputs("\n", fpOut);
		}else{
			strncpy(tmpData, data, len);
			fputs(tmpData, fpOut);
		}

		memset(data, 0, sizeof(data));
		memset(tmpData, 0, sizeof(tmpData));
	}

	fclose(fpOut);
	fclose(fpIn);

	return 0;
}
#endif

int checkConf(){
	FILE *fp = NULL;
	char data[1024] = {0};

	fp = fopen("/tmp/config.decrypt.xml", "r");
	if(fp == NULL){
		CMS_LOG(LL_ERROR, "decrypt xml can't open.\n");
		return -1;
	}

	if(fgets(data, 1024, fp) == NULL){
		CMS_LOG(LL_ERROR, "fgets from  file fail.\n");
		fclose(fp);
		return -1;
	}
	
	fclose(fp);
	
	CMS_LOG(LL_FLOOD, "xml first line is %s\n", data);
	if(strstr(data,"xml version") && strstr(data, "encoding") && strstr(data, "utf-8"))
		return 0;
	else
		return -1;
}

int cmsXml_openSSL_Enc(const char *in,  char *out)
{
	struct stat stat_buf;
	char cmdStr[256] = {0};
	int status = -1;

	sprintf(cmdStr, "openssl aes-128-cbc -e -in %s -out %s -K %s -iv %s", in, out, CMS_XML_KEY, CMS_XML_IV);
    CMS_LOG(LL_INFO, "cmd:%s", cmdStr);
	status = cms_do_cmd(cmdStr);
	if(-1 == status){
		CMS_LOG(LL_ERROR, "enc cmd result is -1!!!");
		return CMS_NOK;
	}else{
		if(WIFEXITED(status)){
			if(0 == WEXITSTATUS(status)){
				CMS_LOG(LL_PRINT, "enc cmd success!!!");
			}else{
				CMS_LOG(LL_ERROR, "enc cmd fail, ret %d!!!", WEXITSTATUS(status));
				return CMS_NOK;
			}
		}else{
			CMS_LOG(LL_ERROR, "enc cmd exit status = %d.", WEXITSTATUS(status));
			return CMS_NOK;
		}	
	}

	if(access(out, F_OK) == -1){
		printf("enc %s fail!!!", in);
		return CMS_NOK;
	}

	memset(&stat_buf, 0, sizeof(struct stat));
	if(stat(out, &stat_buf) == 0){
		if(stat_buf.st_size == 0){
			CMS_LOG(LL_ERROR, "size of %s is 0.", out);
			return CMS_NOK;
		}
	}

	return CMS_OK;
}

int cmsXml_openSSL_Dec(const char *in,  char *out)
{
	struct stat stat_buf;
	char cmdStr[256] = {0};
	int status = -1;

	sprintf(cmdStr, "openssl aes-128-cbc -d -in %s -out %s -K %s -iv %s", in, out, CMS_XML_KEY, CMS_XML_IV);

	status = cms_do_cmd(cmdStr);
	if(-1 == status){
		CMS_LOG(LL_ERROR, "dec cmd result is -1!!!\n");
		return CMS_NOK;
	}else{
		if(WIFEXITED(status)){
			if(0 == WEXITSTATUS(status)){
				CMS_LOG(LL_DEBUG, "dec cmd success!!!\n");
			}else{
				CMS_LOG(LL_ERROR, "dec cmd fail, ret %d!!!\n", WEXITSTATUS(status));
				return CMS_NOK;
			}
		}else{
			CMS_LOG(LL_ERROR, "dec cmd exit status = %d.\n", WEXITSTATUS(status));
			return CMS_NOK;
		}	
	}

	if(access(out, F_OK) == -1){
		CMS_LOG(LL_ERROR, "dec %s fail!!!", in);
		return CMS_NOK;
	}

	memset(&stat_buf, 0, sizeof(struct stat));
	if(stat(out, &stat_buf) == 0){
		if(stat_buf.st_size == 0){
			CMS_LOG(LL_ERROR, "size of %s is 0.\n", out);
			return CMS_NOK;
		}
	}

	return CMS_OK;
}

int parseMultipartData(char *EncrptFilePath)
{
	FILE *fp = NULL;
	FILE *tmpfp = NULL;
	char tmp_configpath[64]={0}, boundary[1024] = {0};
	char *p = NULL, *xml_header = NULL;
	char *fileData = NULL;
	char *tmpfileData = NULL;
	unsigned int iLen = 0, config_len = 0, boundary_len = 0;
	unsigned int nRead = 0, nWrite = 0;
	int ret = 0;
	
	fp = fopen(EncrptFilePath, "r");
	if(fp == NULL){
		CMS_LOG(LL_ERROR, "EncrptFile  can't open.\n");
		return -1;
	}
	fseek(fp, 0, SEEK_END);
	iLen = ftell(fp);
	
	fileData = malloc(iLen + 1);
	if(fileData == NULL){
		CMS_LOG(LL_ERROR, "malloc buff fail.\n");
		fclose(fp);
		return -1;
	}
	fseek(fp,0,SEEK_SET);
	nRead = fread(fileData,1,iLen,fp);
	
	if(strstr(fileData, "Content-Disposition: form-data") == NULL){
		CMS_LOG(LL_ERROR, "no multipart/form-data format!");
		ret = -1;
		goto finally;
	}
	p = strstr(fileData, "\r\n");
	if(p)
	{
		memcpy(boundary, fileData, (p-fileData));
		boundary_len = strlen(boundary);
		CMS_LOG(LL_DEBUG, "boundary=%s, len=%d", boundary, boundary_len);
	}
	else
	{
		CMS_LOG(LL_ERROR, "no boundary data!");
		ret = -1;
		goto finally;
	}
	sprintf(tmp_configpath, "/tmp/tmpconfig.bin.check");
	tmpfp  = fopen(tmp_configpath, "w+");
	if (tmpfp == NULL) {
		ret = -1;
		goto finally;
	}
	xml_header = strstr(fileData, "\r\n\r\n");
	if(xml_header)
	{
		tmpfileData = malloc(iLen);
		if(tmpfileData == NULL)
		{
			ret = -1;
			goto finally;
		}
		//boundary end format: \r\nboundary--\r\n
		config_len = iLen-(xml_header-fileData+4)-(boundary_len + 6);   
		
		CMS_LOG(LL_DEBUG, "config_len=%d", config_len);
		memcpy(tmpfileData, xml_header+4, config_len);
		nWrite = fwrite(tmpfileData, 1, config_len, tmpfp);
	}
	else
	{
		CMS_LOG(LL_ERROR, "not found encrypted xml file!");
		ret = -1;
		goto finally;
	}
	
	CMS_LOG(LL_DEBUG, "nWrite=%d, nRead=%d!\n", nWrite, nRead);
	rename(tmp_configpath, EncrptFilePath);
	
finally:
	fclose(fp);
	free(fileData);
	if(tmpfp)
		fclose(tmpfp);
	if(tmpfileData)
		free(tmpfileData);
	return ret;
}

int cmsXml_decrptConf(char *upgradeFilePath)
{
	/*check if the upgrade file exist.*/
	int ret = 0;
	int fail_count = 0;
	if((upgradeFilePath == NULL) || (access(upgradeFilePath, F_OK) == -1)){
		CMS_LOG(LL_ERROR, "xml file %s not exist.\n", upgradeFilePath);
		cmsXml_add_AlarmID(ALARM_FILE_SERVER_UNREACHABLE);	
		syslog(LOG_ALERT, "Upgrade Alarm %s: File server unreachable",ALARM_FILE_SERVER_UNREACHABLE);
		return -1;
	}

decrpt_Conf:
#if 1
	if(cmsXml_openSSL_Dec(upgradeFilePath,"/tmp/config.decrypt.xml") == CMS_NOK){
#else
	if(decryptXml(upgradeFilePath) == -1){
#endif
		fail_count++;
		if(fail_count == 1)
		{
			CMS_LOG(LL_DEBUG, "parseMultipartData.\n");
			ret = parseMultipartData(upgradeFilePath);
			if(ret == 0){
				goto decrpt_Conf;
			}
		}
		CMS_LOG(LL_ERROR, "encrypt xml decrypt fail.\n");
		cmsXml_add_AlarmID(ALARM_FILE_CONFIG_FORMAT_ERROR);	
		syslog(LOG_ALERT, "Upgrade Alarm %s: Config file format error",ALARM_FILE_CONFIG_FORMAT_ERROR);
		return -1;
	}

	if(checkConf() == -1){
		CMS_LOG(LL_ERROR, "decrypt xml check fail.\n");
		cmsXml_add_AlarmID(ALARM_FILE_CONFIG_FORMAT_ERROR);	
		syslog(LOG_ALERT, "Upgrade Alarm %s: Config file format error",ALARM_FILE_CONFIG_FORMAT_ERROR);
		return -1;
	}
	
	return 0;
}  

int cmsXml_checkCurConfigCRC(void)
{
	FILE *fp1 = NULL, *fp2 = NULL;
	char str1[128] = {0}, str2[128] = {0};
    char cmd[128] = {0};
			
	if(access(CMS_CUR_ENC_CRC, F_OK) == 0){
        snprintf(cmd, sizeof(cmd) - 1, "md5sum %s > %s", CMS_CUR_FILE_ENC, CMS_CUR_ENC_CRC_CHECK);
		cms_do_cmd(cmd);
		
		fp1 = fopen(CMS_CUR_ENC_CRC, "r");
		if(fp1 != NULL && fgets(str1, 128, fp1) != NULL){
			fp2 = fopen(CMS_CUR_ENC_CRC_CHECK, "r");
			if(fp2 != NULL && fgets(str2, 128, fp2) != NULL){
				fclose(fp1);
				fclose(fp2);
			
				if(strcmp(str1, str2) != 0){
                    snprintf(cmd, sizeof(cmd) - 1, "rm %s", CMS_CUR_ENC_CRC_CHECK);
					cms_do_cmd(cmd);
					CMS_LOG(LL_ERROR, "current md5 check sum fail, %s, %s!", str1, str2);
					return CMS_NOK;
				}

                snprintf(cmd, sizeof(cmd) - 1, "rm %s", CMS_CUR_ENC_CRC_CHECK);
				cms_do_cmd(cmd);
				return CMS_OK;
			}else{
				fclose(fp1);
				if(fp2)
					fclose(fp2);
				CMS_LOG(LL_ERROR, "re get current md5 check sum fail!");
				return CMS_NOK;
			}
		}else{
			if(fp1)
				fclose(fp1);
			CMS_LOG(LL_ERROR, "get current md5 check sum fail!");
			return CMS_NOK;
		}
	}else{/*when upgrade from old fw before backup & pre partition added*/
		CMS_LOG(LL_ERROR, "current config md5 checksum file is not exist!");
		return CMS_POK;
	}
}


